package cn.edu.sdjzu.knoverse.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.edu.sdjzu.knoverse.model.dto.NodeTypeDTO;
import cn.edu.sdjzu.knoverse.model.dto.graph.GraphDTO;
import cn.edu.sdjzu.knoverse.model.enums.NodeTypeEnum;
import cn.edu.sdjzu.knoverse.model.vo.result.Result;
import cn.edu.sdjzu.knoverse.service.GraphQueryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.edu.sdjzu.knoverse.model.vo.result.ResultCodeEnum.PARAM_ERROR;

@RestController
@RequestMapping("/graph")
@SaCheckLogin
public class GraphController {
    
    private static final Logger logger = LoggerFactory.getLogger(GraphController.class);

    @Autowired
    private GraphQueryService graphQueryService;
    
    /**
     * 获取某个节点及其邻接图
     * @param id 节点ID
     * @return 包含该节点及其一跳关系节点的图数据，包括：
     *         1. 从中心节点出发的出向关系及目标节点
     *         2. 指向中心节点的入向关系及源节点
     *         3. 邻居节点之间的关系（一度邻居节点之间可能存在的关系）
     *         所有关系都保持正确的指向性（从源到目标）
     */
    @GetMapping("/node/{id}")
    public Result<GraphDTO> getNodeWithNeighbors(@PathVariable("id") Long id) {
        if (id == null) {
            return Result.error(PARAM_ERROR);
        }
        GraphDTO graphData = graphQueryService.getNodeWithNeighbors(id);
        return Result.success(graphData);
    }
    
    /**
     * 根据类型和关键词搜索相关节点及其关联子图
     * @param type 节点类型
     * @param keyword 关键词
     * @return 包含匹配节点及其邻居节点的子图，包括：
     *         1. 从中心节点出发的出向关系及目标节点
     *         2. 指向中心节点的入向关系及源节点
     *         3. 邻居节点之间的关系（一度邻居节点之间可能存在的关系）
     *         所有关系都保持正确的指向性（从源到目标）
     */
    @GetMapping("/subgraph")
    public Result<GraphDTO> getSubgraph(
            @RequestParam(value = "type", required = true) String type,
            @RequestParam(value = "keyword", required = true) String keyword) {
        logger.info("查询子图, 类型: {}, 关键词: {}", type, keyword);
        
        if (type.isEmpty() || keyword.isEmpty()) {
            return Result.error(PARAM_ERROR);
        }
        
        GraphDTO graphData = graphQueryService.getSubgraph(type, keyword);
        return Result.success(graphData);
    }
    
    /**
     * 获取节点的所有属性
     * @param id 节点ID
     * @return 包含节点所有属性的Map
     */
    @GetMapping("/node/{id}/properties")
    public Result<Map<String, Object>> getNodeProperties(@PathVariable("id") Long id) {
        logger.info("查询节点属性, 节点ID: {}", id);
        
        if (id == null) {
            return Result.error(PARAM_ERROR);
        }
        
        Map<String, Object> properties = graphQueryService.getNodeProperties(id);
        if (properties == null) {
            return Result.error(PARAM_ERROR);
        }
        
        return Result.success(properties);
    }
    
    /**
     * 获取所有节点和关系
     * @return 包含所有节点和关系的图数据
     */
    @GetMapping("/all")
    public Result<GraphDTO> getAllNodesAndRelationships() {
        logger.info("获取所有节点和关系");
        GraphDTO graphData = graphQueryService.getAllNodesAndRelationships();
        return Result.success(graphData);
    }
    
    /**
     * 获取所有课程和前置课程关系作为图初始化数据
     * @return 包含所有课程节点和前置课程关系的图数据
     */
    @GetMapping("/courses-init")
    public Result<GraphDTO> getCoursesInitGraph() {
        logger.info("获取所有课程和前置课程关系作为图初始化数据");
        GraphDTO graphData = graphQueryService.getCoursesWithPreCourseRelationships();
        return Result.success(graphData);
    }
    
    /**
     * 获取所有节点类型
     * @return 包含节点类型代码和中文名称的列表
     */
    @GetMapping("/node-types")
    public Result<List<NodeTypeDTO>> getAllNodeTypes() {
        logger.info("获取所有节点类型");
        List<NodeTypeDTO> nodeTypes = Arrays.stream(NodeTypeEnum.values())
                .map(type -> new NodeTypeDTO(type.getType(), type.getName()))
                .collect(Collectors.toList());
        return Result.success(nodeTypes);
    }
}